﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;
using ScrewTurn.Wiki;
using System.Data;
using System.Collections;
using UniBasel.Biozentrum.Utils;
using UniBasel.Biozentrum.NMR;

[ValidationProperty("Count")]  // Serves for validation control. Count is implemented
public partial class Authors : System.Web.UI.UserControl
{
	[Serializable]
	public class Author
	{
		//private string _id;
		//public string ID
		//{
		//    get { return ID; }
		//}

		private string _firstname;
		public string FirstName
		{
			get { return _firstname; }
			set { _firstname = NMRGenUtils.capitalizeAll(value); }
		}

		private string _middlename;
		public string MiddleName
		{
			get { return _middlename; }
			set { _middlename = NMRGenUtils.capitalizeAll(value); }
		}

		private string _lastname;
		public string LastName
		{
			get { return _lastname; }
			set { _lastname = NMRGenUtils.capitalizeAll(value); }
		}

		private string _username;
		public string UserName
		{
			get { return _username; }
			set { _username = value; }
		}

		public string FullName
		{
			get { return ComposeFullName(_firstname, _middlename, _lastname); }
		}

		private Author()
		{
		}

		public Author(string FirstName, string MiddleName, string LastName)
		{
			if (FirstName == null)
				throw new ArgumentNullException("FirstName", "First Name must not be null.");
			if (LastName == null)
				throw new ArgumentNullException("LastName", "Last Name must not be null.");

			_firstname = NMRGenUtils.capitalizeAll(FirstName);
			_lastname = NMRGenUtils.capitalizeAll(LastName);
			_middlename = NMRGenUtils.capitalizeAll(MiddleName);
		}

		//internal Author(object FirstName, object MiddleName, object LastName, object ID)
		//{
		//    string fn = FirstName as string;
		//    string ln = LastName as string;
		//    string mn = MiddleName as string;
		//    string localID = ID.ToString();
		//    if (fn == null || ln == null || localID==null)
		//        throw new ArgumentException("First name or last name or ID is null. Cannot be.");

		//    _firstname = NMRGenUtils.capitalizeAll(fn);
		//    _lastname = NMRGenUtils.capitalizeAll(ln);
		//    _middlename = NMRGenUtils.capitalizeAll(mn);

		//    _id = localID;
		//}
	}

	private OrderedDictionaryEx _selectedauthors = new OrderedDictionaryEx();

	/// <summary>
	/// Contains an ordered list of authors. 
	/// The Dictionary entries must be of two types: <string, string> or <string, author>.
	/// First one is used, when entry is taken from the 'authors' table of NMRExperiments database. 
	/// Then 'key' is an ID and 'value' is a full name 
	/// Second is used, when new author must be created. ('key' is temporary value, 'value' is object of type 'author').
	/// THE EXPLICIT casting is REQUIRED since the Dictionary is of type <object, object>.
	/// </summary>
	public OrderedDictionaryEx SelectedAuthors
	{
		get { return (OrderedDictionaryEx)ViewState["Authors"]; }
	}

	protected void Page_Load(object sender, EventArgs e)
	{
		if (!Page.IsPostBack)
		{
			ViewState["Authors"] = new OrderedDictionaryEx();
		}
		else
		{
			_selectedauthors = (OrderedDictionaryEx)ViewState["Authors"];
			DisplayAllAuthors();
		}

		AuthorsDataSource.SelectParameters["username"].DefaultValue = SessionFacade.Username;
	}

	#region Add Self
	protected void lbSearchSelfName_Click(object sender, EventArgs e)
	{
		if (tbFirstName.Text.Length == 0 && tbMidName.Text.Length == 0 && tbLastName.Text.Length == 0) return;
		panSearchSelfAuthor.Visible = true;
		SearchSelfSQLDataSource.SelectParameters["fnsearchstr"].DefaultValue = (tbFirstName.Text.Length == 0) ? "v" : tbFirstName.Text;
		SearchSelfSQLDataSource.SelectParameters["fnlen"].DefaultValue = tbFirstName.Text.Length.ToString();
		SearchSelfSQLDataSource.SelectParameters["mnsearchstr"].DefaultValue = (tbMidName.Text.Length == 0) ? "v" : tbMidName.Text;
		SearchSelfSQLDataSource.SelectParameters["mnlen"].DefaultValue = tbMidName.Text.Length.ToString();
		SearchSelfSQLDataSource.SelectParameters["lnsearchstr"].DefaultValue = (tbLastName.Text.Length == 0) ? "v" : tbLastName.Text;
		SearchSelfSQLDataSource.SelectParameters["lnlen"].DefaultValue = tbLastName.Text.Length.ToString();
		lvSelfNameSelection.DataBind();
	}

	protected void chkbxAuthorMyself_CheckedChanged(object sender, EventArgs e)
	{
		if (SessionFacade.Username == null) return;

		if (!chkbxAuthorMyself.Checked)
		{
			if (Session["SelfID"] != null) _selectedauthors.Remove(Session["SelfID"]);
			if (panEnterUserName.Visible == true)
			{
				panEnterUserName.Visible = false;
				return;
			}

			lblSelectedAuthors.Controls.Clear();
			DisplayAllAuthors();
			return;
		}

		string fn = null, ln = null, mn = null, id = null; // First Name, Last Name and Middle name and Id will be assigned if present in the database

		using (SqlConnection NMRExperimentsConnection = new SqlConnection(Constants.NMRExperimentsConStr))
		{
			NMRExperimentsConnection.Open();
			SqlCommand QueryGetUser = NMRExperimentsConnection.CreateCommand();
			QueryGetUser.CommandText =
	@"SELECT 
	[id] AS [Id], 
	authfn AS FirstName, 
	authmn AS MiddleName, 
	authln AS LastName 
FROM 
	authors
WHERE
	(authun = @username)";
			QueryGetUser.Parameters.Add(new SqlParameter("@username", SessionFacade.Username));

			using (SqlDataReader UserNameFinder = QueryGetUser.ExecuteReader())
			{
				if (!UserNameFinder.HasRows)
				{
					NMRGenUtils.checkUserCredentials(SessionFacade.Username);
					panEnterUserName.Visible = true;
					tbFirstName.Text = tbMidName.Text = tbLastName.Text = "";
					return;
				}
				int RowsCounter = 0;
				while (UserNameFinder.Read())
				{
					fn = UserNameFinder["FirstName"] as string;
					mn = UserNameFinder["MiddleName"] as string;
					ln = UserNameFinder["LastName"] as string;
					id = UserNameFinder["Id"].ToString();
					RowsCounter++;
				}
				if (RowsCounter != 1) throw new ArgumentException("More than one row was returned for QueryGetUser");
			}
		}

		if (id == null)
		{
			throw new ArgumentException("Id cannot be null");
		}

		if (fn == null || ln == null || fn == "" || ln == "")
		{
			panEnterUserName.Visible = true;
			tbFirstName.Text = (fn == null) ? "" : fn;
			tbMidName.Text = (mn == null) ? "" : mn;
			tbLastName.Text = (ln == null) ? "" : ln;
		}
		else
		{
			Session["SelfID"] = id;
			panEnterUserName.Visible = false;
			_selectedauthors.Add(id, ComposeFullName(fn, mn, ln));
			DisplayAuthor(new KeyValuePair<string, string>(id, (string)_selectedauthors[id]));
		}

		lbCancelSelfSelection_Click(sender, e);
	}

	/// <summary>
	/// Cancels sellection of existing author name
	/// </summary>
	protected void lbCancelSelfSelection_Click(object sender, EventArgs e)
	{
		tbFirstName.Enabled = true;
		tbMidName.Enabled = true;
		tbLastName.Enabled = true;
		tbFirstName.Text = tbLastName.Text = tbMidName.Text = "";
		//panSearchSelfAuthor.Visible = false;
		lbSearchSelfName.Visible = true;
		lbCancelSelfSelection.Visible = false;
		SubmitUserName.CommandArgument = "none";
	}

	protected void SubmitUserName_Command(object sender, CommandEventArgs e)
	{
		if (!Page.IsValid) return;

		// If user name is new, eArg must be 'none', otherwise it will contain an 'Id' of existing author row.
		string eArg = e.CommandArgument.ToString();

		using (SqlConnection NMRExperimentsConnection = new SqlConnection(Constants.NMRExperimentsConStr))
		{
			NMRExperimentsConnection.Open();

			SqlCommand QueryChangeAuthor = NMRExperimentsConnection.CreateCommand();
			if (eArg == "none")
			{
				QueryChangeAuthor.CommandText =
@"INSERT 
INTO 
	authors	  (authfn, authmn, authln, authun)
VALUES        (@FirstName,@MiddleName,@LastName,@UserName)";
				QueryChangeAuthor.Parameters.Add(new SqlParameter("@FirstName", tbFirstName.Text));
				QueryChangeAuthor.Parameters.Add(new SqlParameter("@MiddleName", (tbMidName.Text == null) ? "" : tbMidName.Text));
				QueryChangeAuthor.Parameters.Add(new SqlParameter("@LastName", tbLastName.Text));
				QueryChangeAuthor.Parameters.Add(new SqlParameter("@UserName", SessionFacade.Username));
			}
			else
			{
				QueryChangeAuthor.CommandText =
@"UPDATE
	authors
SET 
	authun = @UserName
WHERE
	(id = @Id) AND (authun IS NULL)";
				QueryChangeAuthor.Parameters.Add(new SqlParameter("@Id", eArg));
				QueryChangeAuthor.Parameters.Add(new SqlParameter("@UserName", SessionFacade.Username));
			}

			QueryChangeAuthor.ExecuteScalar();
		}

		using (SqlConnection NMRUserDataConnection = new SqlConnection(Constants.NMRUserDataConStr))
		{
			NMRUserDataConnection.Open();

			SqlCommand QueryUpdateUser = NMRUserDataConnection.CreateCommand();
			QueryUpdateUser.CommandText =
@"UPDATE
	users
SET
	userfirstname = @FirstName, 
	usermiddlename = @MiddleName, 
	userlastname = @LastName
WHERE
	(username = @UserName)";

			QueryUpdateUser.Parameters.Add(new SqlParameter("@FirstName", tbFirstName.Text));
			QueryUpdateUser.Parameters.Add(new SqlParameter("@MiddleName", (tbMidName.Text == null) ? "" : tbMidName.Text));
			QueryUpdateUser.Parameters.Add(new SqlParameter("@LastName", tbLastName.Text));
			QueryUpdateUser.Parameters.Add(new SqlParameter("@UserName", SessionFacade.Username));

			QueryUpdateUser.ExecuteScalar();
		}

		chkbxAuthorMyself_CheckedChanged(sender, e);
	}

	protected void AddSelfButtonCommand(object sender, CommandEventArgs e)
	{
		string eName = e.CommandName;
		if (eName != "AddSelfCommand") return;
		string eArg = e.CommandArgument.ToString();	// Gets ID
		string senderName = ((LinkButton)sender).Text;   // Gets Full Name

		string[] names = senderName.Split(' '); // Extracts single words from full name

		if (names.Length == 2)
		{
			tbFirstName.Text = names[0];
			tbLastName.Text = names[1];
		}
		else if (names.Length == 3)
		{
			tbFirstName.Text = names[0];
			tbMidName.Text = names[1];
			tbLastName.Text = names[2];
		}
		else
		{
			throw new ArgumentException("AddSelfButtonCommand: Split method returned 1 or more than 3 names.");
		}

		SubmitUserName.CommandArgument = eArg;
		tbFirstName.Enabled = false;
		tbMidName.Enabled = false;
		tbLastName.Enabled = false;
		panSearchSelfAuthor.Visible = false;
		lbSearchSelfName.Visible = false;
		lbCancelSelfSelection.Visible = true;
	}
	protected void lbSearchSelfNameCancel_Click(object sender, EventArgs e)
	{
		//panEnterUserName.Visible = true;
		panSearchSelfAuthor.Visible = false;
	}
	#endregion

	#region Author List

	public int Count
	{
		get { return _selectedauthors.Count; }
	}

	/// <summary>
	/// Clears Author List and collapses the panel
	/// </summary>
	public void Clear()
	{
		chkbxAuthorMyself.Checked = false;
		_selectedauthors.Clear();
		lblSelectedAuthors.Controls.Clear();
		cpeSelectAuthorTable.ClientState = "true";
		cpeSelectAuthorTable.Collapsed = true;
	}

	/// <summary>
	/// Makes form to create new author visible and to select existing invisible
	/// </summary>
	protected void lbtFormCreateNew_Click(object sender, EventArgs e)
	{
		panWrapper.Visible = false;
		panAddAuthor.Visible = true;
	}
	/// <summary>
	/// Makes form to create new author invisible and to select existing visible
	/// </summary>
	protected void CancelNewAuthor_Command(object sender, EventArgs e)
	{
		panWrapper.Visible = true;
		panAddAuthor.Visible = false;
	}

	/// <summary>
	/// Concatenates three fields with spaces
	/// </summary>
	/// <param name="FirstName"></param>
	/// <param name="MidName"></param>
	/// <param name="LastName"></param>
	/// <returns></returns>
	protected static string ComposeFullName(object FirstName, object MidName, object LastName)
	{
		string fn = FirstName as string;
		string ln = LastName as string;
		string mn = MidName as string;
		if (fn == null || ln == null)
			throw new ArgumentException("First name or last name is null. Cannot be.");

		return fn + ((mn == null) ? "" : " " + mn) + " " + ln;
	}

	/// <summary>
	/// Function to display all currently selected authors from Selected Authors dictionary
	/// </summary>
	private void DisplayAllAuthors()
	{
		foreach (DictionaryEntry de in _selectedauthors)
		{
			// Dictionary entry is of 'object' type
			// We better work with 'string'
			string name;
			if (de.Value is Author)
			{
				name = ((Author)de.Value).FullName;
			}
			else if (de.Value is string)
			{
				name = (string)de.Value;
			}
			else
			{
				throw new ArgumentException("de.Value is of unexpected type in DisplayAllAuthors method.");
			}
			KeyValuePair<string, string> x = new KeyValuePair<string, string>((string)de.Key, name);
			DisplayAuthor(x);
		}
	}

	/// <summary>
	/// Function to create control for single author.
	/// </summary>
	/// <param name="x"></param>
	private void DisplayAuthor(KeyValuePair<string, string> x)
	{
		// New Panel Wrapper
		Panel p = new Panel();
		p.ID = "pan" + x.Value + "_" + x.Key;
		lblSelectedAuthors.Controls.Add(p);

		// New Label carring Author Name
		Label t = new Label();
		t.Width = 280;
		t.Text = "<b>" + x.Value + "</b>";

		p.Controls.Add(t);

		// New ImageButton to Remove Author
		ImageButton ibr = new ImageButton();
		ibr.AlternateText = "Remove Author";
		ibr.ID = "ibr" + x.Value + "_" + x.Key;
		ibr.CausesValidation = false;
		ibr.CssClass = "RemoveButton";
		ibr.CommandArgument = x.Key;
		ibr.CommandName = "RemoveAuthorCommand";
		ibr.Command += new CommandEventHandler(RemoveAuthorButtonCommand);
		p.Controls.Add(ibr);

		// New Up and Down Buttons
		ImageButton ibu = new ImageButton();
		ibu.AlternateText = "Move Up Author";
		ibu.ID = "ibu" + x.Value + "_" + x.Key;
		ibu.CausesValidation = false;
		ibu.CssClass = "UpButton";
		ibu.CommandArgument = x.Key;
		ibu.CommandName = "MoveUpAuthorCommand";
		ibu.Command += new CommandEventHandler(MoveAuthor);
		p.Controls.Add(ibu);

		ImageButton ibd = new ImageButton();
		ibd.AlternateText = "Move Down Author";
		ibd.ID = "ibd" + x.Value + "_" + x.Key;
		ibd.CausesValidation = false;
		ibd.CssClass = "DownButton";
		ibd.CommandArgument = x.Key;
		ibd.CommandName = "MoveDownAuthorCommand";
		ibd.Command += new CommandEventHandler(MoveAuthor);
		p.Controls.Add(ibd);

		AsyncPostBackTrigger a = new AsyncPostBackTrigger();
		a.ControlID = ibr.UniqueID;
		a.EventName = "Command";
		upSelectedAuthors.Triggers.Add(a);

		p.Controls.Add(new LiteralControl("<br />"));
	}

	/// <summary>
	///  Moves author up and down in the _selectedauthors ordered dictionary
	/// </summary>
	protected void MoveAuthor(object sender, CommandEventArgs e)
	{
		string eName = e.CommandName;
		string eArg = e.CommandArgument.ToString();
		string cKey = eArg;

		int cIndex = _selectedauthors.GetIndexOf(cKey);
		switch (eName)
		{
			case "MoveUpAuthorCommand":
				_selectedauthors.SwitchTwo(cIndex, cIndex - 1);
				break;
			case "MoveDownAuthorCommand":
				_selectedauthors.SwitchTwo(cIndex, cIndex + 1);
				break;
			default:
				throw new ArgumentException("Incorrect Command Name in MoveAuthor handler.", "eName");
		}
		lblSelectedAuthors.Controls.Clear();
		DisplayAllAuthors();
	}

	/// <summary>
	/// Removes an author from the _selectedauthors ordered dictionary
	/// </summary>
	protected void RemoveAuthorButtonCommand(object sender, CommandEventArgs e)
	{
		string eName = e.CommandName;
		string eArg = e.CommandArgument.ToString();
		if (eName != "RemoveAuthorCommand") return;

		string cValue = (_selectedauthors[eArg] is string) ? (string)_selectedauthors[eArg] : ((Author)_selectedauthors[eArg]).FullName;
		_selectedauthors.Remove(eArg);
		if (eArg == (string)Session["SelfID"]) chkbxAuthorMyself.Checked = false;
		foreach (Control x in lblSelectedAuthors.Controls)
		{
			Panel p = x as Panel;
			if (p == null) continue;
			if (p.ID == ("pan" + cValue + "_" + eArg))
			{
				lblSelectedAuthors.Controls.Remove(p);
				break;
			}
		}
	}

	/// <summary>
	/// Adds new author to _selectedauthors ordered dictionary according to values in a new author name fields
	/// </summary>
	protected void CreateNewAuthorButton_Command(object sender, CommandEventArgs e)
	{
		if (!Page.IsValid) return;
		Author x = new Author(tbNewFN.Text, tbNewMN.Text, tbNewLN.Text);
		string cKey = "New" + NMRGenUtils.GenerateId();
		_selectedauthors.Add(cKey, x);
		DisplayAuthor(new KeyValuePair<string, string>(cKey, ((Author)_selectedauthors[cKey]).FullName));
		tbNewFN.Text = tbNewLN.Text = tbNewMN.Text = "";
	}
	#endregion

	#region Existing Author Selection

	/// <summary>
	/// Update List of existing authors according to value in searh textbox
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	protected void SearchButton_Command(object sender, CommandEventArgs e)
	{
		AuthorsDataSource.SelectParameters["searchstr"].DefaultValue = (tbSearch.Text.Length == 0) ? "v" : tbSearch.Text;
		AuthorsDataSource.SelectParameters["len"].DefaultValue = tbSearch.Text.Length.ToString();
		lvAuthors.DataBind();
		tbSearch.Focus();
	}

	/// <summary>
	/// Adds clicked author field to _selectedauthors dictionary
	/// </summary>
	protected void AddAuthorButtonCommand(object sender, CommandEventArgs e)
	{
		string eName = e.CommandName;
		string eArg = e.CommandArgument.ToString();
		string senderName = ((LinkButton)sender).Text;
		if (eName != "AddAuthorCommand") return;

		_selectedauthors.Add(eArg, senderName); // Add Author to Dictionary
		DisplayAuthor(new KeyValuePair<string, string>(eArg, senderName));
	}
	#endregion
}
